﻿using System;
using System.Collections.Generic;
using System.Text;
using Common.ParameterLocator;
using SpecFlowTfsLinker.Entities;

namespace SpecFlowTfsLinker.Printer
{
    public class StepDefinitionPrinter : ISpecFlowPrinter
    {
        public readonly List<TestCasePrinter> TestCases;
        private readonly UserStory _us;
        private const string _namespaceDeclaration = "Test";
        public StepDefinitionPrinter(UserStory userStory)
        {
            _us = userStory;
            TestCases = new List<TestCasePrinter>();
            _us.TestCases.ForEach(tc =>
                                      {
                                          var tcPrinter = new TestCasePrinter(tc);
                                          InitializeTestCasePrinter(tcPrinter);
                                          TestCases.Add(tcPrinter);
                                      });
        }

        public string UserStoryTitle
        {
            get { return _us.Title.ToPascalCase(); }
        }

        private bool OtherTestCaseContainGivenStatement(TestCasePrinter tc, string statement)
        {
            return TestCases.Exists(x => x != tc && x.Given.Exists(v => v.DisplayMethodStatement.Equals(statement)));
        }

        private bool OtherTestCaseContainWhenStatement(TestCasePrinter tc, string statement)
        {
            return TestCases.Exists(x => x != tc && x.When.Exists(v => v.DisplayMethodStatement.Equals(statement)));
        }

        private bool OtherTestCaseContainThenStatement(TestCasePrinter tc, string statement)
        {
            return TestCases.Exists(x => x != tc && x.Then.Exists(v => v.DisplayMethodStatement.Equals(statement)));
        }

        private void InitializeTestCasePrinter(TestCasePrinter tcPrinter)
        {
            var removeGiven = new List<StatementPrinter>();
            tcPrinter.Given.ForEach(statement =>
                                        {
                                            if (OtherTestCaseContainGivenStatement(tcPrinter, statement.DisplayMethodStatement))
                                            {
                                                removeGiven.Add(statement);
                                            }
                                        });

            removeGiven.ForEach(statement => tcPrinter.Given.Remove(statement));


            var removeWhen = new List<StatementPrinter>();
            ;
            tcPrinter.When.ForEach(statement =>
                                       {
                                           if (OtherTestCaseContainWhenStatement(tcPrinter, statement.DisplayMethodStatement))
                                           {
                                               removeWhen.Add(statement);
                                           }
                                       });

            removeWhen.ForEach(statement => tcPrinter.When.Remove(statement));

            var removeThen = new List<StatementPrinter>();
            ;
            tcPrinter.Then.ForEach(statement =>
                                       {
                                           if (OtherTestCaseContainThenStatement(tcPrinter, statement.DisplayMethodStatement))
                                           {
                                               removeThen.Add(statement);
                                           }
                                       });

            removeThen.ForEach(statement => tcPrinter.Then.Remove(statement));
        }



        public override string ToString()
        {
            var indexTestCase = 1;
            var indexStatement = 1;
            var builder = new StringBuilder();

            builder.Append(Using);
            builder.AppendLine();

            builder.AppendLine(NamespaceDeclaration);
            builder.AppendLine("{");
            builder.AppendLine("[Binding]");
            builder.AppendLine(ClassDeclaration);
            builder.AppendLine("{");

            TestCases.ForEach(testCase =>
                                       {
                                           int index = indexTestCase;
                                           testCase.GivenWhenThen.ForEach(statement =>
                                                                              {
                                                                                  builder.AppendLine(DisPlayMethod(index,
                                                                                                 indexStatement,
                                                                                                 statement));
                                                                                  indexStatement++;
                                                                              });
                                           indexTestCase++;
                                       });

            builder.AppendLine("}");
            builder.AppendLine("}");

            return builder.ToString();
        }

        public string Print()
        {
            return ToString();
        }

        private const string MethodAttribute = "[{0}(@\"{1}\")]";
        private const string MethodDeclaration = "public void TestCase_{0}_{1}_{2}({3})";
        private const string InconclusiveStatement = "Assert.Inconclusive(\"{0}\");";

        private static string Using = "using TechTalk.SpecFlow;" + Environment.NewLine +
                                      "using Microsoft.VisualStudio.TestTools.UnitTesting;";

        private string NamespaceDeclaration
        {
            get
            {
                return "namespace " + _namespaceDeclaration;
            }
        }
        
        private string ClassDeclaration
        {
            get
            {
                return "public class " + UserStoryTitle;
            }
        }

        public static string DisPlayMethod(int testCaseIndex, int statementIndex, StatementPrinter statement)
        {
            var builder = new StringBuilder();
            builder.AppendFormat(MethodAttribute, statement.DisplayType, statement.DisplayMethodStatement);
            builder.AppendLine();
            builder.AppendFormat(MethodDeclaration, testCaseIndex, statement.DisplayType, statementIndex, statement.DisplayParameters);
            builder.AppendLine();
            builder.Append("{");
            builder.AppendLine();
            builder.AppendFormat(InconclusiveStatement,ServiceParameter.Instance.Parameter.InconclusiveMessage);
            builder.AppendLine();
            builder.Append("}");
            return builder.ToString();
        }
    }

    public interface ISpecFlowPrinter
    {
        string Print();
    }
}